package com.jxtc.enterprise.tenant.service;

import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.fastjson.JSONObject;
import com.jxtc.enterprise.common.constants.DeliveryCostTypeEnum;
import com.jxtc.enterprise.common.constants.MealTypeEnum;
import com.jxtc.enterprise.common.dto.DailyOrderCountAndAmountDTO;
import com.jxtc.enterprise.tenant.vo.delliveryCommissionManage.DeliveryCommissionVO;
import com.jxtc.enterprise.tenant.vo.delliveryCommissionManage.DeliveryTaskBriefInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletResponse;
import java.text.MessageFormat;
import java.util.*;

@Service
@Slf4j
public class TenantDeliveryCommissionManageService {

    public List<JSONObject> formatAndCalculateDeliveryTaskDetails(List<DeliveryCommissionVO> deliveryCommissionVOList, Map<String, DailyOrderCountAndAmountDTO> taskIdToOrderCountMap) {

        List<JSONObject> formattedDeliveryCommissions = new ArrayList<>();
        for (DeliveryCommissionVO deliveryCommissionVO : deliveryCommissionVOList) {
            JSONObject formattedData = new JSONObject();
            formattedData.put("taskId", deliveryCommissionVO.getTaskId());
            formattedData.put("riderId", deliveryCommissionVO.getRiderId());
            formattedData.put("riderName", deliveryCommissionVO.getRiderName());
            formattedData.put("deliveryDate", deliveryCommissionVO.getDeliveryDate());
            formattedData.put("mealType", deliveryCommissionVO.getMealType());
            formattedData.put("mealTypeCn", MealTypeEnum.convertMealTypeEnToCn(deliveryCommissionVO.getMealType()));
            formattedData.put("deliveryAddress", deliveryCommissionVO.getDeliveryAddress());

            int orderCount = deliveryCommissionVO.getOrderCount();
            int orderAmount = deliveryCommissionVO.getOrderAmount();
            DailyOrderCountAndAmountDTO countAndAmountDTO = taskIdToOrderCountMap.get(deliveryCommissionVO.getTaskId());
            if (countAndAmountDTO != null) {
                // 如果当前配送任务 ID 有订单数据，则以计算所得的订单数据为准
                // 否则按 delivery_task_t 中记录的数据为准
                orderCount = countAndAmountDTO.getOrderCount();
                orderAmount = countAndAmountDTO.getOrderAmount();
            }

            formattedData.put("orderCount", orderCount);
            String orderTotalPrice = String.format("%.2f", 1.0 * orderAmount / 100);
            formattedData.put("orderTotalPrice", orderTotalPrice);
            String commissionType = buildCommissionTypeString(deliveryCommissionVO.getDeliveryCostType(), deliveryCommissionVO.getDeliveryCostValue());
            formattedData.put("commissionType", commissionType);
            double commission = calcCommission(deliveryCommissionVO.getDeliveryCostType(), deliveryCommissionVO.getDeliveryCostValue(),
                    orderCount, orderAmount);
            formattedData.put("commission", String.format("%.2f", commission));
            String additionalFee = String.format("%.2f", 1.0 * deliveryCommissionVO.getAdditionalFee() / 100);
            formattedData.put("additionalFee", additionalFee);
            String deductFee = String.format("%.2f", 1.0 * deliveryCommissionVO.getDeductFee() / 100);
            formattedData.put("deductFee", deductFee);
            String finalCommission = calcFinalCommission(deliveryCommissionVO.getDeliveryCostType(), deliveryCommissionVO.getDeliveryCostValue(),
                    orderCount, orderAmount,
                    deliveryCommissionVO.getAdditionalFee(), deliveryCommissionVO.getDeductFee());
            formattedData.put("finalCommission", finalCommission);
            // todo 基于 配送状态和结算状态，得到 配送任务的状态；1: 待配送；2：已送达(未结算)；3: 已结算
            int taskStatus = convertTaskStatus(deliveryCommissionVO.getDeliveryStatus(), deliveryCommissionVO.getSettlementStatus());
            formattedData.put("status", taskStatus);
            formattedData.put("note", deliveryCommissionVO.getNote());

            formattedDeliveryCommissions.add(formattedData);
        }

        return formattedDeliveryCommissions;
    }

    public List<JSONObject> formatDeliveryTaskDetails(List<DeliveryCommissionVO> deliveryCommissionVOList) {

        List<JSONObject> formattedDeliveryCommissions = new ArrayList<>();
        for (int k = 0; k < deliveryCommissionVOList.size(); k++) {
            DeliveryCommissionVO deliveryCommissionVO = deliveryCommissionVOList.get(k);
            JSONObject formattedData = new JSONObject();
            formattedData.put("taskId", deliveryCommissionVO.getTaskId());
            formattedData.put("riderId", deliveryCommissionVO.getRiderId());
            formattedData.put("riderName", deliveryCommissionVO.getRiderName());
            formattedData.put("deliveryDate", deliveryCommissionVO.getDeliveryDate());
            formattedData.put("mealType", deliveryCommissionVO.getMealType());
            formattedData.put("mealTypeCn", MealTypeEnum.convertMealTypeEnToCn(deliveryCommissionVO.getMealType()));
            formattedData.put("deliveryAddress", deliveryCommissionVO.getDeliveryAddress());
            formattedData.put("orderCount", deliveryCommissionVO.getOrderCount());
            String orderTotalPrice = String.format("%.2f", 1.0 * deliveryCommissionVO.getOrderAmount() / 100);
            formattedData.put("orderTotalPrice", orderTotalPrice);
            String commissionType = buildCommissionTypeString(deliveryCommissionVO.getDeliveryCostType(), deliveryCommissionVO.getDeliveryCostValue());
            formattedData.put("commissionType", commissionType);
            double commission = calcCommission(deliveryCommissionVO.getDeliveryCostType(), deliveryCommissionVO.getDeliveryCostValue(),
                    deliveryCommissionVO.getOrderCount(), deliveryCommissionVO.getOrderAmount());
            formattedData.put("commission", String.format("%.2f", commission));
            String additionalFee = String.format("%.2f", 1.0 * deliveryCommissionVO.getAdditionalFee() / 100);
            formattedData.put("additionalFee", additionalFee);
            String deductFee = String.format("%.2f", 1.0 * deliveryCommissionVO.getDeductFee() / 100);
            formattedData.put("deductFee", deductFee);
            String finalCommission = calcFinalCommission(deliveryCommissionVO.getDeliveryCostType(), deliveryCommissionVO.getDeliveryCostValue(),
                    deliveryCommissionVO.getOrderCount(), deliveryCommissionVO.getOrderAmount(),
                    deliveryCommissionVO.getAdditionalFee(), deliveryCommissionVO.getDeductFee());
            formattedData.put("finalCommission", finalCommission);
            // todo 基于 配送状态和结算状态，得到 配送任务的状态；1: 待配送；2：已送达(未结算)；3: 已结算
            int taskStatus = convertTaskStatus(deliveryCommissionVO.getDeliveryStatus(), deliveryCommissionVO.getSettlementStatus());
            formattedData.put("status", taskStatus);
            formattedData.put("note", deliveryCommissionVO.getNote());

            formattedDeliveryCommissions.add(formattedData);
        }

        return formattedDeliveryCommissions;
    }

    private int convertTaskStatus(int deliveryTask, int settlementStatus) {

        int taskStatus = 1;
        if (deliveryTask == 1 && settlementStatus == 1) {
            taskStatus = 1; // 待配送
        }
        if (deliveryTask == 2 && settlementStatus == 1) {
            taskStatus = 2; // 已送达(未结算)
        }
        if (settlementStatus == 2) {
            taskStatus = 3; // 已结算
        }

        return taskStatus;
    }

    private String buildCommissionTypeString(String costTypeEn,int costValue) {
        String typeNameCn = "未知";

        switch(costTypeEn) {
            case "PER_ORDER":
                String commssionValueStr1 = String.format("%.2f", 1.0 * costValue / 100);
                typeNameCn = MessageFormat.format("每单{0}元", commssionValueStr1);
                break;
            case "PERCENTAGE":
                String commssionValueStr2 = String.format("%.2f", 1.0 * costValue / 100);
                typeNameCn = MessageFormat.format("订单总金额{0}{1}", commssionValueStr2, "%");
                break;
            case "FIXED_AMOUNT":
                typeNameCn = String.format("包干%.2f元", 1.0 * costValue / 100);
                break;
        }

        return typeNameCn;
    }


    /**
     * 单位为分
     * @param deliveryCostType
     * @param deliveryCostValue
     * @param orderCount
     * @param orderAmount
     * @return
     */
    private double calcCommission(String deliveryCostType, int deliveryCostValue, int orderCount, int orderAmount) {

        double totalCommission = 0;
        // 1. 根据 佣金 方式，计算基于配送订单的费用
        if (deliveryCostType.equalsIgnoreCase(DeliveryCostTypeEnum.PER_ORDER.getName())) {
            totalCommission += 1.0 * orderCount * deliveryCostValue / 100; // 单位是分，转换成元需要除以 100
        }
        else if (deliveryCostType.equalsIgnoreCase(DeliveryCostTypeEnum.PERCENTAGE_OF_ORDER.getName())) {
            // 订单总金额单位是分，此处除以100，转换成元; 百分比存放的是 万分之一 的单位，比如 5% 数据库中存放的是 500，需要除以100000
            totalCommission += (1.0 * orderAmount / 100) * (1.0 * deliveryCostValue / 10000);
        }
        else if (deliveryCostType.equalsIgnoreCase(DeliveryCostTypeEnum.FIXED_AMOUNT.getName())) {
            totalCommission += 1.0 * deliveryCostValue / 100; // 单位是分，转换成元需要除以 100
        } else {
            totalCommission = Double.MAX_VALUE;
        }

        return totalCommission;
    }

    private String calcFinalCommission(String deliveryCostType, int deliveryCostValue, int orderCount, int orderAmount, int additionalFee, int deductFee) {

        // 1. 根据 佣金 方式，计算基于配送订单的费用
        double totalCommission = calcCommission(deliveryCostType, deliveryCostValue, orderCount, orderAmount);
        // 2. 加项
        totalCommission += additionalFee;
        // 3. 扣除项
        totalCommission -= deductFee;
        // 4. 将分转换成元，格式化成字符串，并保留两位小数 作为返回值
        return String.format("%.2f", totalCommission);
    }


    public JSONObject formatDeliveryTaskBriefInfo(List<DeliveryTaskBriefInfoVO> deliveryTaskBriefInfoVOS) {

        List<JSONObject> formattedTaskBriefInfoList = new ArrayList<>();
        // key为公司id-公司名 拼接的字符串，value为为该公司供餐的店铺列表
        HashMap<String, List<JSONObject>> companyToStoreMap = new HashMap<>();
        for (int k = 0; k < deliveryTaskBriefInfoVOS.size(); k++) {
            DeliveryTaskBriefInfoVO deliveryTaskBriefInfoVO = deliveryTaskBriefInfoVOS.get(k);
            String companyIdAndName = deliveryTaskBriefInfoVO.getCompanyId() + "-" + deliveryTaskBriefInfoVO.getCompanyName();
            JSONObject storeIdAndNameObj = new JSONObject();
            storeIdAndNameObj.put("storeId", deliveryTaskBriefInfoVO.getStoreId());
            storeIdAndNameObj.put("storeName", deliveryTaskBriefInfoVO.getStoreName());
            if (companyToStoreMap.containsKey(companyIdAndName)) {
                companyToStoreMap.get(companyIdAndName).add(storeIdAndNameObj);
            } else {
                List<JSONObject> stores = new ArrayList<>();
                stores.add(storeIdAndNameObj);
                companyToStoreMap.put(companyIdAndName, stores);
            }
        }

        // 理论上，返回的数据行，companyId-companyName 只会有一个，此处兼容可能存在的脏数据，最终返回给前端时，仅返回列表中的第一个
        for (Map.Entry<String, List<JSONObject>> stringListEntry : companyToStoreMap.entrySet()) {
            String companyId = stringListEntry.getKey().split("-")[0];
            String companyName = stringListEntry.getKey().split("-")[1];
            JSONObject formattedTaskBriefInfo = new JSONObject();
            formattedTaskBriefInfo.put("companyId", companyId);
            formattedTaskBriefInfo.put("companyName", companyName);
            formattedTaskBriefInfo.put("supplyStores", stringListEntry.getValue());
            formattedTaskBriefInfoList.add(formattedTaskBriefInfo);
        }

        if (formattedTaskBriefInfoList.size() > 1) {
            log.error("companyId-companyName 组合只能有一个，但是此处存在多个，可能存在脏数据. companyToStoreMap: {}", JSONObject.toJSONString(companyToStoreMap));
        }

        return formattedTaskBriefInfoList.get(0);
    }

    public String formatFenToYuan(int fenOfPrice) {

        double yuanOfPrice = fenOfPrice * 1.0 / 100;
        return String.format("%.2f", yuanOfPrice);
    }

    /**
     * 页面查询条件传递的是 yyyy-MM-dd hh:mm:ss 格式的查询开始和结束时间，delivery_task_t 表中配送日期，开始时间点，是两个不同列，因此，此处需要将入参拆分成两个字段，这样才能作为sql的入参
     * @param startDateTime
     * @param endDateTime
     * @return
     */
    public JSONObject parseDeliveryDateAndTime(String startDateTime, String endDateTime) {

        JSONObject result = new JSONObject();
        String[] startDateTimeArrays = startDateTime.split(" ");
        if (startDateTimeArrays.length == 2) {
            result.put("startDate", startDateTimeArrays[0]);
            result.put("startTime", startDateTimeArrays[1]);
        } else {
            if (StringUtils.hasText(startDateTime)) {
                result.put("startDate", startDateTimeArrays[0]);
                result.put("startTime", "00:00:00");
            } else {
                result.put("startDate", "2024-01-01");
                result.put("startTime", "00:00:00");
            }
        }

        String[] endDateTimeArrays = endDateTime.split(" ");
        if (endDateTimeArrays.length == 2) {
            result.put("endDate", endDateTimeArrays[0]);
            result.put("endTime", endDateTimeArrays[1]);
        } else {
            if (StringUtils.hasText(endDateTime)) {
                result.put("endDate", endDateTimeArrays[0]);
                result.put("endTime", "23:59:59");
            } else {
                result.put("endDate", "2124-01-01");
                result.put("endTime", "23:59:59");
            }
        }

        return result;
    }

    public JSONObject parseDeliveryAndSettlementStatusFromTaskStatus(int taskStatus) {
        int deliveryStatus;
        int settlementStatus;
        // 1 和 2 表示 配送状态，3和4 表示结算状态
        switch(taskStatus) {
            // 任务状态为1时，配送状态为 待配送，结算状态为 未结算
            case 1:
                deliveryStatus = 1;
                settlementStatus = 1;
                break;
            // 任务状态为2时，配送状态为 已送达，结算状态为 未结算
            case 2:
                deliveryStatus = 2;
                settlementStatus = 1;
                break;
            // 任务状态为3时，配送状态为 已送达，结算状态为 待结算(未结算)
            case 3:
                deliveryStatus = 2;
                settlementStatus = 1;
                break;
            // 任务状态为4时，配送状态为 已送达，结算状态为 已结算
            case 4:
                deliveryStatus = 2;
                settlementStatus = 2;
                break;
            // delivery_task_t 表中，配送状态有效值1和2，结算状态有效值为1和2，当页面传递0时，表示查询所有配送状态和所有 结算状态的 任务，此时sql中添加对这两个状态字段的过滤条件
            default:
                deliveryStatus = 0;
                settlementStatus = 0;
        }

        JSONObject result = new JSONObject();
        result.put("deliveryStatus", deliveryStatus);
        result.put("settlementStatus", settlementStatus);

        return result;
    }

    public void exportDeliveryTaskDetailToExcel(List<JSONObject> formattedDeliveryTaskDetails, HttpServletResponse response) {

        try (ExcelWriter writer = ExcelUtil.getWriter(true)) {

            List<Map<String, Object>> data = new ArrayList<>();
            int idx = 1;
            for (JSONObject taskDetail : formattedDeliveryTaskDetails) {
                data.add(createRow(idx++, taskDetail));
            }
            String sheetName = "配送任务详情";
            addSheet(writer, sheetName, data, true);
            String filename = MessageFormat.format("{0}-{1}.xlsx", "配送任务详情", System.currentTimeMillis());

            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + filename);
            writer.flush(response.getOutputStream(), true);
        } catch (Exception e) {
            e.printStackTrace();
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            throw new RuntimeException("Error while generating Excel file", e);
        }
    }

    private Map<String, Object> createRow(Integer index, JSONObject taskDetail) {
        // 使用 LinkedHashMap 而不是 HashMap 防止标题栏乱序
        Map<String, Object> row = new LinkedHashMap<>();
        row.put("序号", index);
        row.put("姓名", taskDetail.getString("riderName"));
        row.put("配送日期", taskDetail.getString("deliveryDate"));
        row.put("配送时段", taskDetail.getString("mealType"));
        row.put("配送任务", taskDetail.getString("deliveryAddress"));
        row.put("订单份数", taskDetail.getIntValue("orderCount"));
        row.put("费用类型", taskDetail.getString("commissionType"));
        row.put("订单金额", taskDetail.getString("orderTotalPrice"));
        row.put("配送费用", taskDetail.getString("commission"));
        row.put("费用加项", taskDetail.getString("additionalFee"));
        row.put("费用扣除项", taskDetail.getString("deductFee"));
        row.put("实发佣金", taskDetail.getString("finalCommission"));
        row.put("任务状态", taskDetail.getIntValue("status"));
        row.put("备注", taskDetail.getString("note"));
        return row;
    }

    private void addSheet(ExcelWriter writer, String sheetName, List<Map<String, Object>> data, boolean firstSheet) {
        if (firstSheet) {
            writer.renameSheet(sheetName);
        } else {
            writer.setSheet(sheetName);
        }
        writer.write(data, true);

        // 获取当前工作表
        Sheet sheet = writer.getSheet();

        // 设置“餐品”这一列的宽度，索引从 0 开始
        int dishNameColumnIndex = 3;
        sheet.setColumnWidth(dishNameColumnIndex, 40 * 256);

        // 设置“下单时间”这一列的宽度
        int orderTimeColumnIndex = 5;
        sheet.setColumnWidth(orderTimeColumnIndex, 20 * 256);
    }
}
